package org.jzyan.framework.ratelimit.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jzyan.framework.core.exception.BusinessException;
import org.jzyan.framework.ratelimit.RateLimiterService;
import org.jzyan.framework.ratelimit.annotation.RateLimit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @FileName : RateLimitAspect
 * @Version : 1.0
 * @Description : 接口限流
 * @Author : jzyan
 * @CreateDate : 2019/12/24 16:18
 */
@Aspect
@Component
public class RateLimitAspect implements EnvironmentAware {
    private Environment environment;
    @Autowired
    private RateLimiterService rateLimiterService;

    @Pointcut("execution(* *(..)) && @annotation(org.jzyan.framework.ratelimit.annotation.RateLimit)")
    public void aspect() {
    }

    @Around("aspect()")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //获取目标方法
        Method targetMethod = methodSignature.getMethod();
        //获取目标方法的@RateLimit注解
        RateLimit rateLimit = targetMethod.getAnnotation(RateLimit.class);
        boolean bl = rateLimiterService.tryAcquire(this.environment.resolvePlaceholders(rateLimit.name()), rateLimit.rateType(), rateLimit.rate(),
                rateLimit.seconds());
        if (!bl) {
            throw new BusinessException(HttpStatus.TOO_MANY_REQUESTS.value(), "请求频繁，请稍后再试");
        }
        return point.proceed();
    }

    /**
     * Set the {@code Environment} that this component runs in.
     *
     * @param environment
     */
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

}
